package com.ofwiki.demo.scheduler.common;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.support.CronSequenceGenerator;

import java.util.Date;
import java.util.TimeZone;

/**
 * @author HuangJS
 * @date 2021-12-11 12:23 下午
 */
public class DynamicCronTrigger implements Trigger {
    private final static Logger LOGGER = LoggerFactory.getLogger(DynamicCronTrigger.class);
    private Object jobId;
    private AbstractDynamicCronHandler dynamicCronHandler;
    private String cronExpression;
    private TimeZone timeZone = TimeZone.getDefault();
    private CronSequenceGenerator sequenceGenerator;
    
    public DynamicCronTrigger(Object jobId, AbstractDynamicCronHandler dynamicCronHandler) {
        this.jobId = jobId;
        this.dynamicCronHandler = dynamicCronHandler;
    }
    
    public DynamicCronTrigger(Object jobId, TimeZone timeZone, AbstractDynamicCronHandler dynamicCronHandler) {
        this.jobId = jobId;
        this.timeZone = timeZone;
        this.dynamicCronHandler = dynamicCronHandler;
    }
    
    @Override
    public Date nextExecutionTime(TriggerContext triggerContext) {
        String cronExpression = dynamicCronHandler.getCronExpression(jobId);
        if (cronExpression == null) {
            return null;
        }
        if (this.sequenceGenerator == null || !cronExpression.equals(this.cronExpression)) {
            try {
                this.sequenceGenerator = new CronSequenceGenerator(cronExpression, this.timeZone);
                this.cronExpression = cronExpression;
            } catch (Exception e) {
                LOGGER.error(e.getMessage(), e);
            }
        }
    
        Date date = triggerContext.lastCompletionTime();
        if (date != null) {
            Date scheduled = triggerContext.lastScheduledExecutionTime();
            if (scheduled != null && date.before(scheduled)) {
                // Previous task apparently executed too early...
                // Let's simply use the last calculated execution time then,
                // in order to prevent accidental re-fires in the same second.
                date = scheduled;
            }
        } else {
            date = new Date();
        }
        return this.sequenceGenerator.next(date);
    }
}
